package com.foxhis.xms.wildfly.logger;

import com.aliyun.openservices.aliyun.log.producer.*;
import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
import com.aliyun.openservices.log.common.LogItem;
import org.jboss.logmanager.ExtHandler;
import org.jboss.logmanager.ExtLogRecord;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.logging.ErrorManager;
import java.util.logging.Formatter;

public class AliyunLogHandler extends ExtHandler {
    /**
     * 应用名称，一般可设置为域名
     */
    private String application;
    /**
     * 日志服务参数
     */
    private String logProject;
    private String logStore;
    private String logEndpoint;
    private String logAccessKeyId;
    private String logAccessKeySecret;

    private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS");

    private ProducerConfig producerConfig = new ProducerConfig();
    private ProjectConfig projectConfig;
    private Producer producer = null;

    public String getApplication() {
        return application;
    }

    public void setApplication(String application) {
        this.application = application;
    }

    public String getLogProject() {
        return logProject;
    }

    public void setLogProject(String logProject) {
        this.logProject = logProject;
    }

    public String getLogStore() {
        return logStore;
    }

    public void setLogStore(String logStore) {
        this.logStore = logStore;
    }

    public String getLogEndpoint() {
        return logEndpoint;
    }

    public void setLogEndpoint(String logEndpoint) {
        this.logEndpoint = logEndpoint;
    }

    public String getLogAccessKeyId() {
        return logAccessKeyId;
    }

    public void setLogAccessKeyId(String logAccessKeyId) {
        this.logAccessKeyId = logAccessKeyId;
    }

    public String getLogAccessKeySecret() {
        return logAccessKeySecret;
    }

    public void setLogAccessKeySecret(String logAccessKeySecret) {
        this.logAccessKeySecret = logAccessKeySecret;
    }

    public Producer createProducer() {
        projectConfig = buildProjectConfig();
        Producer producer = new LogProducer(producerConfig);
        producer.putProjectConfig(projectConfig);
        return producer;
    }

    private ProjectConfig buildProjectConfig() {
        return new ProjectConfig(logProject, logEndpoint, logAccessKeyId, logAccessKeySecret);
    }

    public void closeProducer() {
        try {
            if (producer != null) {
                producer.close();
            }
        } catch (Exception e) {
            reportError("Failed to close LoghubAppender.", e, ErrorManager.FORMAT_FAILURE);
        }
    }

    @Override
    protected void doPublish(final ExtLogRecord record) {
        if (producer == null) {
            producer = createProducer();
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    closeProducer();
                }
            });
        }
        final String formatted;
        final Formatter formatter = getFormatter();
        try {
            formatted = formatter.format(record);
        } catch (Exception ex) {
            reportError("Formatting error", ex, ErrorManager.FORMAT_FAILURE);
            return;
        }
        if (formatted.length() == 0) {
            // nothing to write; don't bother
            return;
        }


        LogItem logItem = new LogItem();
        logItem.PushBack("application", application);
        logItem.PushBack("hostname", record.getHostName());
        logItem.PushBack("timestamp", dtf.format(LocalDateTime.now()));
        logItem.PushBack("level", record.getLevel().getName());
        logItem.PushBack("file", record.getSourceFileName());
        logItem.PushBack("line", String.valueOf(record.getSourceLineNumber()));
        logItem.PushBack("class", record.getSourceClassName());
        logItem.PushBack("method", record.getSourceMethodName());
        logItem.PushBack("process", String.valueOf(record.getProcessId()) + ":" + record.getProcessName());
        logItem.PushBack("thread", String.valueOf(record.getThreadID()) + ":" + record.getThreadName());

        Map<String, String> mdc = record.getMdcCopy();
        for (String key : mdc.keySet()) {
            logItem.PushBack("mdc_" + key, mdc.get(key));
        }
        logItem.PushBack("context", formatted);

        try {
            producer.send(projectConfig.getProject(), logStore, "wildfly", "", logItem,
                    new Callback() {
                        @Override
                        public void onCompletion(Result result) {
                            if (!result.isSuccessful()) {
                                reportError("Failed to send log, project=" + logProject + ", logStore=" + logStore + ", logItem=" + logItem + ", errorCode=" + result.getErrorCode() + ", errorMessage=" + result.getErrorMessage(), null, ErrorManager.FORMAT_FAILURE);
                            }
                        }
                    });
        } catch (Exception e) {
            reportError("Failed to send log, project=" + logProject + ", logItem=" + logItem, e, ErrorManager.FORMAT_FAILURE);
        }
    }

    @Override
    public void close() throws SecurityException {
        super.close();
        closeProducer();
    }
}
